#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <omp.h>
#include <math.h>

/*
#paragm omp parallel for private()
#pragma omp atomic
omp_get_num_procs();
*/

/*4145*/

int separa_arq(char nome[]);
int separa_arq_frase(char nome[]);
int tamanho_max_word(char nome[]);
int tamanho_max_phrase(char nome[]);
int palindromo(char s[]);
void crivo(int *primos,float max);
int isprimo(int *primos,char s[],int max);

int main(int argc, char *argv[])
{
    int max_palavra;
    int max_frase;
    int *primos;
    int i;
    int max_procs = omp_get_num_procs(); /*numero maximo de processos paralelos que serão criados*/
    char *palavra,arq[10];
    FILE *fp = NULL;
    int npal = 0,nprime = 0;
    double inicio = 0,fim = 0;
    double tempo_serial, tempo_paralelo;

    /* TEXTO GRANDE - PARALELO*/

    max_palavra = tamanho_max_word("wikipedia.txt")+1; /*tamanho da maior palavra encontrada + 1*/
    primos = malloc(sizeof(int)*((max_palavra*90)+1)); /* aloca o vetor de primos*/
    separa_arq("wikipedia.txt"); /* separa o arquivo*/
    crivo(primos,(max_palavra*90)+1); /* cria o vetor de primos */
    inicio = omp_get_wtime(); /* inicio do tempo */

#pragma omp parallel firstprivate(arq,fp,max_palavra) private(i,palavra) reduction(+:npal) reduction(+:nprime) shared(primos) default(none)
    {
        palavra = malloc(sizeof(char)*max_palavra); /*inicializa a string com o maior valor encontrado no texto*/
        i = omp_get_thread_num(); /* pega o id da thread para  */
        sprintf(arq,"%d",i);
        strcat(arq,".txt");
        fp = fopen(arq,"r");/* abre o arquivo referente ao id da threads  thread 1 abre 1.txt */
        while(fscanf(fp,"%s",palavra)!=EOF)
        {
            if(palindromo(palavra)==1)
            {
#pragma omp atomic
                npal++; /*caso a palavra seja palindro incrementa 1*/
#pragma omp atomic
                nprime += isprimo(primos,palavra,strlen(palavra)); /*caso a soma dos ASCII seja primo incrementa 1*/
            }
        }
        free(palavra);
    }
    fim = omp_get_wtime(); /* fim do tempo */
    tempo_paralelo = fim-inicio; /* tempo de processamento*/

    printf("PARALELO - WIKI - PAL: %d    PRIMOS: %d   TEMPO: %lf\n",npal,nprime,tempo_paralelo);

    /* TEXTO GRANDE - SERIAL */

    /* zera as variaveis inicio,fim npal,nprime */
    inicio = fim = 0;
    npal = 0;
    nprime = 0;

    palavra = malloc(sizeof(char)*max_palavra); /* inicia o vetor com o tamanho da maior palavra encontrada no texto*/
    inicio = omp_get_wtime(); /*  */
    for(i = 0; i < max_procs; i++)
    {
        sprintf(arq,"%d",i);
        strcat(arq,".txt");
        fp = fopen(arq,"r"); /* le o arquivo */
        while(fscanf(fp,"%s",palavra)!=EOF)
        {
            if(palindromo(palavra)==1)
            {
                npal++;
                nprime += isprimo(primos,palavra,strlen(palavra));
            }
        }
    }

    fim = omp_get_wtime();
    tempo_serial = fim-inicio;
    printf("SERIAL   - WIKI - PAL: %d    PRIMOS: %d   TEMPO: %lf\nSPEEDUP: %lf",npal,nprime,tempo_serial,tempo_serial/tempo_paralelo);

    /*TEXTO MENOR - PARALELO - palavra*/

    npal = 0;
    max_palavra = tamanho_max_word("shakespe.txt")+1; /* maior palavra encontrada no texto */
    separa_arq("shakespe.txt"); /*separa o arquivo no numero de threads possiveis*/
    inicio = omp_get_wtime();
#pragma omp parallel firstprivate(arq,fp,max_palavra) private(i,palavra) reduction(+:npal) default(none)
    {
        npal = 0;
        palavra = malloc(sizeof(char)*max_palavra); /* aloca string com o maior tamanho de palavra*/
        i = omp_get_thread_num();/*pega o id da thread*/
        sprintf(arq,"%d",i);
        strcat(arq,".txt");
        fp = fopen(arq,"r");
        while(fscanf(fp,"%s",palavra)!=EOF)
        {
#pragma omp atomic
            npal += palindromo(palavra);
        }
        free(palavra);
    }
    fim = omp_get_wtime();
    tempo_paralelo = fim - inicio;
    printf("\n\nPARALELO - SHAKE - PAL-PALAVRA: %d   TEMPO: %lf",npal,tempo_paralelo);

    /*TEXTO MENOR - SERIAL - PALAVRA*/
    npal = 0;
    palavra = malloc(sizeof(char)*max_palavra); /* aloca string com o tamanho da maior palavra */
    inicio = omp_get_wtime();
    for(i = 0; i < max_procs; i++)
    {
        sprintf(arq,"%d",i);
        strcat(arq,".txt");
        fp = fopen(arq,"r"); /* le arquivo */
        while(fscanf(fp,"%s",palavra)!=EOF)
        {
            npal += palindromo(palavra);/*conta os palindromos*/
        }
    }
    fim = omp_get_wtime();
    tempo_serial = fim - inicio;
    printf("\nSERIAL   - SHAKE - PAL-PALAVRA: %d   TEMPO: %lf\nSPEEDUP: %lf\n",npal,tempo_serial,tempo_serial/tempo_paralelo);
    free(palavra);

    /*TEXTO MENOR - PARALELO - FRASE  */
    npal = 0;
    max_frase = tamanho_max_phrase("shakespe.txt")+1; /* qtde de caracteres na maior frase do texto + 1*/
    separa_arq_frase("shakespe.txt"); /* sepera o arquivo */
    inicio = omp_get_wtime();
#pragma omp parallel firstprivate(arq,fp,max_frase) private(i,palavra) reduction(+:npal) default(none)
    {
        npal = 0;
        palavra = malloc(sizeof(char)*max_frase); /* aloca a string com a qtde de caracteres na maior frase */
        i = omp_get_thread_num();
        sprintf(arq,"%d",i);
        strcat(arq,".txt");
        fp = fopen(arq,"r"); /* le arquivo */
        while(fscanf(fp,"%s",palavra)!=EOF)
        {
#pragma omp atomic
            npal += palindromo(palavra); /*conta palindromos*/
        }
        free(palavra);
    }
    fim = omp_get_wtime();
    tempo_paralelo = fim - inicio;
    printf("\n\nPARALELO - SHAKE - PAL-FRASE: %d   TEMPO: %lf",npal,tempo_paralelo);
    //free(primos);

    /*TEXTO MENOR - SERIAL FRASE */
    npal = 0;
    palavra = malloc(sizeof(char)*max_frase); /* aloca string com a qtde de caracteres da maior frase*/
    inicio = omp_get_wtime();
    for(i=0; i<max_procs; i++)
    {
        sprintf(arq,"%d",i);
        strcat(arq,".txt");
        fp = fopen(arq,"r");
        while(fscanf(fp,"%s",palavra)!=EOF)
        {
            npal += palindromo(palavra); /*conta palindromos*/
        }
    }
    fim = omp_get_wtime();
    tempo_serial = fim-inicio;
    printf("\nSERIAL   - SHAKE - PAL-FRASE: %d    TEMPO: %lf\nSPEEDUP: %lf",npal,tempo_serial,tempo_serial/tempo_paralelo);
    free(primos);
    free(palavra);


    return 0;
}

/*
Função int isprimo(int *primos,char s[],int max) procura no vetor de primos e retorna o valor
0 - nao primo;
1 - primo;
*/
int isprimo(int *primos,char s[],int max)
{
    int i,k;
    k = 0;
    for(i = 0; i<max; i++)
    {
        k += (int)s[i];
    }
    return primos[k];
}

/*
Função void crivo(int *primos,float max) cacula o crivo de erastostenes e atribui os valores
0 - nao primo
1 - primo
*/
void crivo(int *primos,float max)
{
    int n;
    int i,j;
    n = (int)sqrt(max);
    int k = (int) max;
    for(i = 0; i<max; i++)
    {
        primos[i] = 1;
    }
    for(i = 2; i < n; i++)
    {
        if(primos[i] == 1)
        {
            //#pragma omp parallel for private(j) firstprivate(i,k,primos) default(none)
            for(j = i + 1; j < k; j++)
            {
                if((j%i)==0)
                    primos[j] = 0;
            }
        }
    }
}


/*
Função int palindromo(char s[]) verefica se a palavra é palindromo retorna
0 - nao palindro
1 - palindromo
 */
int palindromo(char s[])
{

    int j,k;
    if(strlen(s)<3) return 0;

    for(j = 0,k = strlen(s)-1; j <= k; j++,k--)
    {
        if(s[j] != s[k])
            return 0;
    }
    //printf("%s\n",s);
    return 1;
}

/*
Funcao int tamanho_max_word(char nome[]) retorna tamanho da maior palavra encontrada
*/
int tamanho_max_word(char nome[])
{
    FILE *fp = NULL;
    int count = 0;
    int max = 0;
    char c;
    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        count++;
        if(isspace(c))
        {
            if(count > max) max = count;
            count = 0;
        }
    }
    fclose(fp);
    return max;
}

/*
Função int tamanho_max_phrase(char nome[]) retorna tamanho da maior frase encontrada
*/
int tamanho_max_phrase(char nome[])
{
    FILE *fp = NULL;
    int count = 0;
    int max = 0;
    char c;
    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        count++;
        if(c == '.' || c == '!' || c == '?')/*ao encontrar '.'  '!' ou '?' consideramos que a frase acaba*/
        {
            if(count > max) max = count;
            count = 0;
        }
    }
    fclose(fp);
    return max;
}
/*
Funcao int separa_arq(char nome[]) separa os arquivos no numero de processos que podem ser alocado pela omp
divide o arquivo em relação a qtde de pontos finais no arquivo
*/
int separa_arq(char nome[])
{
    FILE *fp = NULL;
    FILE *ftk = NULL;

    int count = 0;
    int div = 0;
    int num_procs = omp_get_num_procs();/*pega o numero de processos*/
    int min_procs = 0;

    char c;
    char arq[10];

    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        if(c == '.')count++; /* conta pontos finais */
    }
    /*tratamento para que em cada arquivo contenha palavras inteiras*/
    div = count/num_procs; /*divide num de pontos por processos*/
    div++; /*soma um para arredondar*/

    rewind(fp);
    count = 0;
    sprintf(arq,"%d",min_procs);
    strcat(arq,".txt");
    ftk = fopen(arq,"w");

    /*rele o arquivo principal e cria num_processos de arquivos*/
    while(!feof(fp))
    {
        c = fgetc(fp);
        if(isalpha(c) || isspace(c))/*consideramos apenas letras nos arquivos - se for alfabetico ou espaço é colocado em UPPER CASE*/
        {
            fputc(toupper(c),ftk);
        }
        else
        {
            if(c == '.')/*verfica se é '.' para o controle de qtde de pontos em cada arquivo*/
            {
                count++;
                fputc(' ',ftk); /* substitui o ponto por espaço ' ' */
                fputc(toupper(c),ftk);
            }

        }


        if(count == div) /* verifica se a qtde de pontos é a mesma da divisão dos pontos em arquivos*/
        {
            count = 0; /*reseta a variavel para 0 */
            fclose(ftk); /* fecha o arquivo atual*/
            min_procs++; /* conta em qual arquivo deve ser inserido */
            sprintf(arq,"%d",min_procs);
            strcat(arq,".txt");
            ftk = fopen(arq,"w");/* abre novo arquivo com o novo indice*/
        }
    }
    fclose(ftk);
    fclose(fp);
    return 0;
}
/*
Funcao int separa_arq_frase(char nome[]) separa os arquivos no numero de processos que podem ser alocado pela omp
divide o arquivo em relação a qtde de pontos finais no arquivo
*/
int separa_arq_frase(char nome[])
{
    FILE *fp = NULL;
    FILE *ftk = NULL;

    int count = 0;
    int div = 0;
    int num_procs = omp_get_num_procs(); /* numero de processos */
    int min_procs = 0; /*estado minimo*/


    char c;
    char arq[10];

    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        if(c == '.' || c =='!' || c == '?')count++; /* caso encontre '.' '!' ou '?' incremente a variavel count -
        isso significa que frases acabam em '.' '!' '?'*/
    }
    div = count/num_procs; /* qtde de frases no arquivo*/
    div++;
    rewind(fp);

    count = 0;

    sprintf(arq,"%d",min_procs);
    strcat(arq,".txt");
    ftk = fopen(arq,"w");

    while(!feof(fp))
    {
        c = fgetc(fp);
        if(isalpha(c) || isspace(c)) /* se for espaco - ' ' ou alfanumero  colacamos em UPPER CASE*/
        {
            if(c != ' ')
                fputc(toupper(c),ftk);
        }
        else
        {
            if(ispunct(c))
            {
                if(c == '.' || c =='!' || c == '?') /* caso termine a frase incrementa count e poe ' ' no lugar */
                {
                    count++;
                    fputc(' ',ftk);
                }


            }
        }


        if(count == div) /* verifica se a qtde de frases por arquivo atingido*/
        {
            count = 0;
            fclose(ftk);
            min_procs++;
            sprintf(arq,"%d",min_procs);
            strcat(arq,".txt");
            ftk = fopen(arq,"w");
        }
    }
    fclose(ftk);
    fclose(fp);
    return 0;
}

